package top.zhangjianyong.tools.service.api.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import top.zhangjianyong.tools.entity.marirui.*;
import top.zhangjianyong.tools.exception.MairuiException;
import top.zhangjianyong.tools.service.api.MairuiService;
import top.zhangjianyong.tools.service.mybatis.*;
import top.zhangjianyong.tools.utils.ApiResponse;
import top.zhangjianyong.tools.utils.MaiRuiApiUtil;
import top.zhangjianyong.tools.utils.MailUtil;
import top.zhangjianyong.tools.utils.MairuiUtil;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.nio.charset.Charset;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * @author ： zhangjianyong
 * @date ： 2022/10/13 8:59 AM
 */
@Slf4j
@Service
public class MairuiServiceImpl implements MairuiService {

    @Autowired
    private IJJService ijjService;
    @Autowired
    private IJJGKService ijjgkService;
    @Autowired
    private MaiRuiApiUtil maiRuiApiUtil;
    @Autowired
    private IJJGZService ijjgzService;
    @Autowired
    private IJJHOTService ijjhotService;
    @Autowired
    private IJJHOTCCService ijjhotccService;
    @Value("${mairui.zxl}")
    private BigDecimal ZXL;

    private final static ThreadLocal<Map<String, BigDecimal>> JZB = new ThreadLocal<>();

    @Override
    public ApiResponse all() {
        List<JJ> jjs = maiRuiApiUtil.JJAll();
        log.info("[获取全部] 数量 {}", jjs.size());
        for (JJ jj : jjs) {
            JJ one = ijjService.qryOneByDm(jj.getDm());
            if (one == null) {
                ijjService.save(jj);
            } else {
                jj.setId(one.getId());
                ijjService.updateById(jj);
            }
        }
        return ApiResponse.success();
    }

    /**
     * 基金概况
     *
     * @param dm
     * @return
     */
    @Override
    public ApiResponse jjgk(String dm) {
        JJGK jjgk = maiRuiApiUtil.JJGK(dm);
        JJGK one = ijjgkService.qryOneByDm(jjgk.getDm());
        if (one == null) {
            ijjgkService.save(jjgk);
        } else {
            jjgk.setId(one.getId());
            ijjgkService.updateById(jjgk);
        }
        return ApiResponse.success(jjgk);
    }

    /**
     * 价值比
     *
     * @param dm
     * @return
     */
    @Override
    public ApiResponse gpjz(String dm) {
        return ApiResponse.success(gpjzb(dm));
    }

    /**
     * 获取股票价值比
     *
     * @param dm
     * @return
     */
    private BigDecimal gpjzb(String dm) {
        if (dm.contains(".")) {
            dm = dm.split("\\.")[0];
        }
        //检查缓存
        Map<String, BigDecimal> map = JZB.get();
        if (CollectionUtil.isEmpty(map)) {
            map = new HashMap<>();
            JZB.set(map);
        }
        //存在直接返回
        if (map.containsKey(dm)) {
            log.info("[缓存命中] {}", dm);
            return map.get(dm);
        }

        List<HSCPJDLR> hscpjdlr = maiRuiApiUtil.HSCPJDLR(dm);
        //计算近一年每股收益
        BigDecimal mgsy = getMgsy(hscpjdlr);

        //计算价值
        BigDecimal rp = mgsy.divide(ZXL.divide(new BigDecimal(100), 4, RoundingMode.HALF_UP), 4, RoundingMode.HALF_UP);
        HSRLSSJY hsrlssjy = maiRuiApiUtil.HSRLSSJY(dm);
        BigDecimal p = hsrlssjy.getP();
        if (p.compareTo(BigDecimal.ZERO) == 0) throw new MairuiException("股价为0");
        if (rp.compareTo(BigDecimal.ZERO) == 0) return new BigDecimal(Integer.MAX_VALUE);
        BigDecimal jzb = p.divide(rp, 4, RoundingMode.HALF_UP);
        map.put(dm, jzb);
        return jzb;
    }

    /**
     * 获取最近4季度每股利润
     *
     * @param hscpjdlr
     * @return
     */
    private BigDecimal getMgsy(List<HSCPJDLR> hscpjdlr) {
        List<HSCPJDLR> collect = hscpjdlr.stream().limit(5).collect(Collectors.toList());
        if (collect.size() < 5) throw new MairuiException("查询到季度利润数量不足5");
        //当年
        HSCPJDLR c = collect.get(0);
        int hYear = c.year() - 1;
        int jd = c.jd();
        if (jd == 4) {
            return c.ettege();
        }
        BigDecimal cMg = c.ettege();

        HSCPJDLR h1 = collect.stream()
                .filter(o -> o.jd() == 4 && o.year() == hYear)
                .findFirst().orElseThrow(() -> new MairuiException("未找到季度利润"));
        HSCPJDLR h2 = collect.stream()
                .filter(o -> o.jd() == jd && o.year() == hYear)
                .findFirst().orElseThrow(() -> new MairuiException("未找到季度利润"));
        BigDecimal hMg = h1.ettege().subtract(h2.ettege());
        return cMg.add(hMg);

    }

    @Override
    public ApiResponse jjgz(String dm) {
        return ApiResponse.success(getJJGZ(dm));
    }

    /**
     * 获取基金估值
     *
     * @param dm
     * @return
     */
    public JJGZ getJJGZ(String dm) {
        log.info("[基金代码] {}", dm);
        JJGK jjgk = ijjgkService.qryOneByDm(dm);
        if (jjgk == null) {
            jjgk = maiRuiApiUtil.JJGK(dm);
            ijjgkService.save(jjgk);
        }
        String lx = jjgk.getLx();
        log.info("[基金类型] {}", lx);
       /* if (MaiRuiNoGzLx.noGz(lx)) {
            return null;
        }*/

        JJGZ jjgz = ijjgzService.qryOneByDmAndGzDate(dm, LocalDate.now());
        if (jjgz == null) {
            JJ jj = ijjService.qryOneByDm(dm);
            jjgz = new JJGZ();
            jjgz.setDm(dm);
            jjgz.setGzDate(LocalDate.now());
            jjgz.setMc(jj.getMc());
        }

        //获取股票持仓
        List<JJGPCC> jjgpcc = maiRuiApiUtil.JJGPCC(dm);
        //计算估值
        jjgz(jjgpcc, jjgz);

        ijjgzService.saveOrUpdate(jjgz);
        return jjgz;
    }

    /**
     * 加权计算持仓估值
     *
     * @param jjgpcc
     * @param jjgz
     * @return
     */
    private JJGZ jjgz(List<JJGPCC> jjgpcc, JJGZ jjgz) {

        //持仓为空直接按1：1折算返回
        if (CollectionUtil.isEmpty(jjgpcc)) {
            log.info("[基金持仓] 持仓为空");
            jjgz.setGz(new BigDecimal(100));

            jjgz.setRemark(JSONObject.toJSONString(jjgpcc));
            jjgz.setZxl(ZXL);
            jjgz.setCcb(BigDecimal.ZERO);
            return jjgz;
        }

        jjgpcc = getCurrentGpcc(jjgpcc);
        log.info("[最新持仓] {}", JSONObject.toJSONString(jjgpcc));
        BigDecimal value = BigDecimal.ZERO;//真实价值

        StringBuilder ccSb = new StringBuilder();
        //所有持仓占的比例
        BigDecimal ccb = BigDecimal.ZERO;
        //持仓折算真实价值
        for (JJGPCC cc : jjgpcc) {
            String dm1 = cc.getDm();
            String mc = cc.getMc();
            BigDecimal jzbl = cc.jzbl();
            //五位代码为港股
            if (dm1.trim().length() == 5) {
                log.info("[港股] {} 1:1折算", dm1);
                //以1：1折算
                value = new BigDecimal(100).multiply(jzbl).add(value);
                continue;
            }
            try {
                BigDecimal jzb = gpjzb(dm1);
                log.info("[价值比] {}:{}", dm1, jzb);
                cc.setJzb(jzb);
                value = new BigDecimal(100).multiply(jzbl)
                        .divide(jzb, 4, RoundingMode.HALF_UP).add(value);
                ccb = ccb.add(jzbl);
                ccSb.append(mc).append(":").append(cc.getJzbl()).append(",");
            } catch (MairuiException e) {
                log.info("[折算异常] {}", e.getMessage());
                //计算每股收益的时候失败，以1：1折算
                value = new BigDecimal(100).multiply(jzbl).add(value);
            }

        }

        if (ccb.compareTo(BigDecimal.ONE) > 0) throw new MairuiException("所有持仓比例和大于1");
        //非持仓比例按1：1折算回真实价值
        value = BigDecimal.ONE.subtract(ccb).multiply(new BigDecimal(100)).add(value);

        log.info("[基金估值] {}", value);
       /* double v = MathUtil.customLog(100, value.doubleValue());
        log.info("[基金估值] 求log {}", v);
        if (Double.isNaN(v)) {
            jjgz.setGz(new BigDecimal(-1).setScale(4, RoundingMode.HALF_UP));
        } else {
            jjgz.setGz(new BigDecimal(v).setScale(4, RoundingMode.HALF_UP));
        }*/
        jjgz.setGz(value);
        jjgz.setRemark(JSONObject.toJSONString(jjgpcc));
        jjgz.setZxl(ZXL);
        jjgz.setCcb(ccb);
        jjgz.setCc(ccSb.toString());
        return jjgz;
    }

    /**
     * 获取最新一期的持仓
     *
     * @param jjgpcc
     * @return
     */
    private List<JJGPCC> getCurrentGpcc(List<JJGPCC> jjgpcc) {
        String t = jjgpcc.stream().map(JJGPCC::getT)
                .max(String::compareTo)
                .orElseThrow(() -> new MairuiException("获取股票持仓失败"));
        return jjgpcc.stream().filter(o -> t.equals(o.getT())).collect(Collectors.toList());
    }

    /**
     * 批量估值
     *
     * @param num
     * @return
     */
    @Override
    public ApiResponse jjgzpl(Integer num) {

        try {
            List<JJ> list = ijjService.listNotGzLimit(num);
            List<JJGZ> collect = list.stream()
                    .map(JJ::getDm)
                    .map(this::getJJGZ)
                    .collect(Collectors.toList());
            try {
                String meaasge = MairuiUtil.getMessage(collect);
                MailUtil.send("批量估值", meaasge);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                throw new RuntimeException(e);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            try {
                MailUtil.send("批量估值异常", e.getMessage());
            } catch (Exception ex) {
                log.error(ex.getMessage(), ex);
                throw new RuntimeException(ex);
            }
        }


        return ApiResponse.success();
    }


    /**
     * 根据传入的代码估值
     *
     * @param dms
     * @return
     */
    @Override
    public ApiResponse jjgzpl(List<String> dms) {
        List<JJGZ> collect = dms.stream()
                .map(this::getJJGZ)
                .sorted((o1, o2) -> o2.getGz().compareTo(o1.getGz()))
                .collect(Collectors.toList());

        try {
            String message = MairuiUtil.getMessage(collect);
            MailUtil.send("持有估值", message);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        return ApiResponse.success();
    }

    /**
     * 导入热门基金
     *
     * @param file
     * @return
     * @throws IOException
     */
    //@Transactional
    @Override
    public ApiResponse jjHot(MultipartFile file) throws IOException {
        List<String> lines = IoUtil.readLines(file.getInputStream(), Charset.defaultCharset(), new ArrayList<>());
        //清空数据库
        //ijjhotService.removeAll();
        //保存热门基金
        List<JJHOT> jjhots = lines.stream().map(line -> {
            try {
                log.info("[热门数据] {}", line);
                JJHOT hot = createHot(line);
                JJHOT one = ijjhotService.qryOneByDm(hot.getDm());
                if (one == null) {
                    ijjhotService.save(hot);
                } else {
                    hot.setId(one.getId());
                    ijjhotService.updateById(hot);
                }

                return hot;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return null;
            }

        }).filter(Objects::nonNull).collect(Collectors.toList());
        log.info("[热门基金] 本次导入{}", jjhots.size());

        return ApiResponse.success(jjhots);
    }

    private JJHOT createHot(String line) {
        String[] split = line.split(",");
        Integer sort = Integer.valueOf(split[0]);
        String[] jj = split[1].split("\\.");
        String dm = jj[0];
        String mc = jj[1];
        Integer hot = Integer.valueOf(split[2]);
        JJHOT jjhot = new JJHOT();
        jjhot.setSort(sort);
        jjhot.setDm(dm);
        jjhot.setMc(mc);
        jjhot.setHot(hot);
        return jjhot;
    }

    @Override
    public ApiResponse jjHotGz() {

        List<JJGZ> jjgzs = jjHotGzAll();
        try {
            MailUtil.send("热门基金", MairuiUtil.getMessage(jjgzs));
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }

        return ApiResponse.success(MairuiUtil.getMessage(jjgzs));
    }

    public List<JJGZ> jjHotGzAll() {
        //获取全部热门基金
        List<JJHOT> list = ijjhotService.list();
        log.info("[热门基金] 全部{}", list.size());
        //计算估值
        List<JJGZ> jjgzs = list.stream()
                .map(hot -> {
                    try {
                        return getJJGZ(hot.getDm());
                    } catch (MairuiException e) {
                        log.error(e.getMessage(), e);
                        return null;
                    }
                })
                .filter(Objects::nonNull)
                .sorted((o1, o2) -> o2.getGz().compareTo(o1.getGz()))
                .collect(Collectors.toList());
        return jjgzs;
    }

    @Override
    public ApiResponse jjHotCC(LocalDate valueDate) {
        List<JJGZ> list = ijjgzService.listByValueDate(valueDate);
        Map<String, JJHOTCC> map = new HashMap<>();
        list.stream().map(JJGZ::getCc).forEach(cc -> {
            if (StringUtils.isBlank(cc)) return;
            System.out.println(cc);
            String[] split = cc.split(",");
            for (String s : split) {
                if (s.contains(":")) {
                    String[] ccc = s.split(":");
                    putCCMap(ccc, valueDate, map);
                }
            }
        });
        Collection<JJHOTCC> values = map.values();
        BigDecimal total = values.stream().map(JJHOTCC::getCc).reduce(BigDecimal.ZERO, BigDecimal::add);
        values.stream().forEach(v -> {
            BigDecimal divide = v.getCc().divide(total, 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100));
            v.setCc(divide);
            //保存
            JJHOTCC one = ijjhotccService.qryOne(v.getMc(), v.getValueDate());
            if (one == null) {
                ijjhotccService.save(v);
            } else {
                v.setId(one.getId());
                ijjhotccService.updateById(v);
            }
        });

        List<JJHOTCC> collect = values.stream().sorted(((o1, o2) -> o2.getCc().compareTo(o1.getCc())))
                .collect(Collectors.toList());

        return ApiResponse.success(collect);
    }

    private void putCCMap(String[] ccc, LocalDate valueDate, Map<String, JJHOTCC> map) {
        String replace = ccc[1].replace("%", "");
        String name = ccc[0];
        if (map.containsKey(name)) {
            JJHOTCC gpcc = map.get(name);
            BigDecimal add = gpcc.getCc().add(new BigDecimal(replace));
            gpcc.setCc(add);
        } else {
            JJHOTCC jjhotcc = new JJHOTCC();
            jjhotcc.setMc(name);
            jjhotcc.setCc(new BigDecimal(replace));
            jjhotcc.setValueDate(valueDate);
            map.put(name, jjhotcc);
        }
    }

    @Override
    public ApiResponse jjHotUp() {
        List<JJHOT> list = ijjhotService.list();
        List<JJHOTGZUP> jjhotgzups = new ArrayList<>();
        for (JJHOT jjhot : list) {
            JJHOTGZUP jjhotgzup = new JJHOTGZUP();
            String dm = jjhot.getDm();
            log.info("[代码] {}", dm);
            jjhotgzup.setDm(dm);
            jjhotgzup.setMc(jjhot.getMc());

            List<JJGZ> jjgzs = ijjgzService.qryByDm(dm);
            if (CollectionUtil.isEmpty(jjgzs)) continue;
            BigDecimal max = jjgzs.stream().map(JJGZ::getGz).max((Comparator.naturalOrder())).get();

            log.info("[max] {}", max);
            jjhotgzup.setMax(max);
            BigDecimal min = jjgzs.stream().map(JJGZ::getGz).min((Comparator.naturalOrder())).get();
            log.info("[min] {}", min);
            jjhotgzup.setMin(min);
            BigDecimal avg = max.add(min).divide(new BigDecimal(2), 4, RoundingMode.HALF_UP);
            log.info("[avg] {}", min);

            BigDecimal last = jjgzs.stream().max(Comparator.comparing(JJGZ::getGzDate)).map(JJGZ::getGz).get();
            log.info("[last] {}", last);
            jjhotgzup.setLast(last);
            BigDecimal up = last.subtract(avg).divide(avg, 4, RoundingMode.HALF_UP);
            log.info("[up] {}", up);
            jjhotgzup.setUp(up);
            jjhotgzups.add(jjhotgzup);
        }

        List<JJHOTGZUP> res = jjhotgzups.stream().sorted(Comparator.comparing(JJHOTGZUP::getUp))
                .collect(Collectors.toList());
        return ApiResponse.success(res);
    }

    @Override
    public ApiResponse jjGpcc(String code) {
        code = code.replaceAll("sh", "")
                .replaceAll("sz", "");
        //获取最新
        List<JJGPCC> jjgpcc = maiRuiApiUtil.JJGPCC(code);
        if (CollectionUtil.isEmpty(jjgpcc))
            return ApiResponse.success(jjgpcc);
        String t = jjgpcc.get(0).getT();
        List<JJGPCC> collect = jjgpcc.stream().filter(cc -> t.equals(cc.getT()))
                .collect(Collectors.toList());

        //循环获取
        List<Future<JJGPCCDTO>> list = new ArrayList<>();
        for (JJGPCC cc : collect) {

            Future<JJGPCCDTO> submit = MairuiUtil.submit(() -> {
                JJGPCCDTO dto = new JJGPCCDTO();
                BeanUtils.copyProperties(cc, dto);
                HSRLSSJY hsrlssjy = maiRuiApiUtil.HSRLSSJY(cc.getDm());
                dto.setHsrlssjy(hsrlssjy);
                return dto;
            });

            list.add(submit);
        }

        List<JJGPCCDTO> jjgpccdtos = new ArrayList<>();
        for (Future<JJGPCCDTO> future : list) {
            try {
                JJGPCCDTO jjgpccdto = future.get();
                jjgpccdtos.add(jjgpccdto);
            } catch (InterruptedException | ExecutionException e) {
                log.error(e.getMessage(), e);
                throw new RuntimeException(e);
            }
        }
        return ApiResponse.success(jjgpccdtos);
    }
}
